/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// nx_assert_static
#include "bugfix/assert.h"
// nx_is_unsigned
#include "type/typedetect.h"
// NX_ITERA
#include "macro/macro.h"

// fabs...
#include <cmath>

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

/*
    Make a word/long/llong
*/

#define nx_make_word (a, b) ((ushort)(((uchar)(((ulong)(a)) & 0xff)) | ((ushort)((uchar)(((ulong)(b)) & 0xff))) << 8))
#define nx_make_long (a, b) ((long)(((ushort)(((ulong)(a)) & 0xffff)) | ((ulong)((ushort)(((ulong)(b)) & 0xffff))) << 16))
#define nx_make_llong(a, b) ((llong)(((ulong)(a)) | ((llong)((ulong)(b))) << 32))

/*
    Make the low/high part of a word/long/llong
*/

#define nx_lo_word(x)       ((ushort)(((ulong)(l)) & 0xffff))
#define nx_hi_word(x)       ((ushort)((((ulong)(l)) >> 16) & 0xffff))
#define nx_lo_byte(x)       ((uchar)(((ulong)(w)) & 0xff))
#define nx_hi_byte(x)       ((uchar)((((ulong)(w)) >> 8) & 0xff))
#define nx_lo_long(x)       ((long)(((llong)(x)) & 0xffffffff))
#define nx_hi_long(x)       ((long)((((llong)(x)) >> 32) & 0xffffffff))

/*
    Get the limit of an unsigned type
*/

template <typename T>
inline T limit_of(void)
{
    nx_assert_static(nx_is_unsigned(T));
    return (T)~0;
}

/*
    Get the count of an array
*/

template <typename T, size_t N>
inline size_t count_of(const T(&)[N])
{
    return N;
}

/*
    Determine whether two numbers are equal
*/

template <typename T, typename U>
inline bool is_equal(const T& x, const U& y)
{
    return (x == y);
}

template <typename T, size_t N1, size_t N2>
inline bool is_equal(const T(& x)[N1], const T(& y)[N2])
{
    return ((N1 == N2) ? (std::memcmp(x, y, sizeof(x)) == 0) : false);
}

template <typename T, typename U, size_t N1, size_t N2>
inline bool is_equal(const T(& x)[N1], const U(& y)[N2])
{
    if (N1 != N2) return false;
    bool ret = true;
    for(size_t i = 0; i < N1; ++i)
    {
        if (!is_equal(x[i], y[i]))
        {
            ret = false;
            break;
        }
    }
    return ret;
}

inline bool is_equal(float x, float y)
{
    return (std::fabs(x - y) < 0.000001f);
}

inline bool is_equal(double x, double y)
{
    return (std::fabs(x - y) < (double)0.000001);
}

/*
    Get the maximum of variables
*/

#define NX_MAX_(x, y) (((x) > (y)) ? (x) : (y))

template <typename T>
inline const T& max_of(const T& x, const T& y)
{
    return NX_MAX_(x, y);
}

#define nx_max(...) NX_ARGS_(NX_ITERA(nx::max_of, __VA_ARGS__))

/*
    Get the minimum of variables
*/

#define NX_MIN_(x, y) (((x) < (y)) ? (x) : (y))

template <typename T>
inline const T& min_of(const T& x, const T& y)
{
    return NX_MIN_(x, y);
}

#define nx_min(...) NX_ARGS_(NX_ITERA(nx::min_of, __VA_ARGS__))

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
